using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using System.Reflection;

namespace SCG.General
{
    public class SourceCodeCompiler
    {
        public SourceCodeCompiler()
        {
        }

        //Access the compiler errors
        public CompilerErrorCollection CompilerErrors
        {
            get
            {
                return _errors;
            }
        }

        //Compile the source and return an instance, or null if compiler errors
        public object CompileSource(string source, string className, string references)
        {
            //Get instance of compiler and related objects
            CodeDomProvider CodeDom = new Microsoft.CSharp.CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters();

            //Set parameters
            parameters.GenerateExecutable = false;
            parameters.GenerateInMemory = true;
            parameters.IncludeDebugInformation = false;

            //Add assembly references
            TypeLoader asmLoader = new TypeLoader("Object", "");

            //Add the default ones
            parameters.ReferencedAssemblies.Add("System.dll");
            parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
            parameters.ReferencedAssemblies.Add("System.Drawing.dll");

            //Add ones in the root folder
            foreach (Assembly asm in asmLoader.Assemblies)
                if (!parameters.ReferencedAssemblies.Contains(asm.Location))
                    parameters.ReferencedAssemblies.Add(asm.Location);

            //Add custom ones specified in parameter call
            string[] customReferences = references.Split(';');
            foreach (string reference in customReferences)
                if (reference.Trim() != "")
                {
                    string refWithExt = reference;
                    if (!refWithExt.ToUpper().EndsWith("EXE"))
                        if (!refWithExt.ToUpper().EndsWith(".DLL"))
                            refWithExt = refWithExt + ".dll";
                    if (!parameters.ReferencedAssemblies.Contains(refWithExt))
                        parameters.ReferencedAssemblies.Add(refWithExt);
                }

            //Compile the source
            CompilerResults results;
            results = CodeDom.CompileAssemblyFromSource(parameters, source);

            //Errors
            _errors = results.Errors;
            if (results.Errors.HasErrors)
                return null;

            //Get the assembly
            Assembly asmCompiled = results.CompiledAssembly;

            //Look for the derived type
            object obj = null;
            foreach (Type t in asmCompiled.GetTypes())
            {
                if (!t.IsAbstract && t.BaseType != null && t.BaseType.Name == className)
                {
                    obj = Activator.CreateInstance(t);
                    break;
                }
            }

            //Return resulting instance
            return obj;
        }

        //private members
        private CompilerErrorCollection _errors = null;
    }
}
